package org.groovymud.engine;

/* Copyright 2008 Matthew Corby-Eaglen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import net.wimpi.telnetd.TelnetD;

import org.apache.commons.net.nntp.NewGroupsOrNewsQuery;
import org.apache.log4j.Logger;
import org.groovymud.engine.event.HeartBeatListener;
import org.groovymud.object.MudObject;
import org.groovymud.object.alive.Player;
import org.groovymud.object.registry.MudObjectAttendant;
import org.groovymud.object.registry.Registry;
import org.groovymud.object.registry.ScriptScanner;
import org.groovymud.shell.telnetd.LoginShell;
import org.groovymud.shell.telnetd.ShellBridge;
import org.groovymud.utils.CountingMap;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public abstract class JMudEngine extends Thread {

    private static final String NET_DEAD_APPEND = " (net dead)";

    static final int DEFAULT_MUD_HEARTBEAT_LENGTH_MS = 1000;

    private static TelnetD telnetDaemon;

    private Registry objectRegistry;

    private static boolean running;
    
    private final static Logger logger = Logger.getLogger(JMudEngine.class);

    private static AbstractApplicationContext context;

    private MudObjectAttendant objectAttendant;

    private CountingMap netDeadPlayers;

    private boolean shutdownRequested = false;

    public JMudEngine() {    	
        running = true;
    }

    public static void main(String[] args) {
        logger.info("workspace.loc:" + System.getProperty("workspace.loc"));
        logger.info("mavenrepo:" + System.getProperty("maven.repo"));
        logger.info("mudspace:" + System.getProperty("mudspace"));
        context = new ClassPathXmlApplicationContext(new String[] { "org/groovymud/telnetdContext.xml", "org/groovymud/applicationContext.xml", "org/groovymud/commandAliases.xml" });
        context.registerShutdownHook();
        logger.info("Caching scripts...");
        ScriptScanner scanner = (ScriptScanner) context.getBean("scriptScanner");
        scanner.scan();
        logger.info("done.");
        JMudEngine engine = (JMudEngine) context.getBean("mudEngine");
        // hack to support spring
        ShellBridge.setContext(context);
        engine.start();
        telnetDaemon.start();
    }

    public void run() {
        logger.info("GroovyMud up and running");
        while (isRunning()) {
            try {
                doHeartBeat();
                handleTheNetDead();
                checkPlayerHandles();
                if (isShutdownRequested()) {
                    createShutdownBehaviour().handleShutdown();
                }
                Thread.sleep(DEFAULT_MUD_HEARTBEAT_LENGTH_MS);
            } catch (InterruptedException e) {
                logger.error(e, e);
            }
        }

    }

    protected void checkPlayerHandles() {
        // TODO Auto-generated method stub
        CountingMap netDead = getNetDeadPlayers();
        Set<Player> totallyDead = netDead.getKeysAbove(30);

        for (Player p : totallyDead) {
            p.dest(false);
        }

    }

    protected void doHeartBeat() {
        /* here we do all the object actions and update everything */
        Iterator<MudObject> i = getObjectRegistry().getItems().iterator();
        while (i.hasNext()) {
            MudObject o = i.next();
            if (o instanceof HeartBeatListener) {
                ((HeartBeatListener) o).heartBeat();
            }
        }
    }

    protected void handleTheNetDead() {
        Iterator<LoginShell> x = new HashSet<LoginShell>(getObjectRegistry().getActivePlayerHandles()).iterator();
        CountingMap netDead = getNetDeadPlayers();
        while (x.hasNext()) {
            LoginShell shell = x.next();
            if (!shell.getConnection().isActive()) {
                Player p = getObjectRegistry().getPlayerByHandle(shell);
                if (!netDead.containsKey(p)) {
                    p.setName(p.getName() + NET_DEAD_APPEND);
                }
                netDead.increment(p);
            }
        }
    }

    public static boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        JMudEngine.running = running;
    }

    public void requestShutDown(Player requester) {
        logger.info("shutdown requested!!!");
       
        shutdownRequested = true;
      
    }

    public static AbstractApplicationContext getContext() {
        return context;
    }

    public Registry getObjectRegistry() {
        return objectRegistry;
    }

    public void setObjectRegistry(Registry registry) {
        this.objectRegistry = registry;
    }

    public MudObjectAttendant getObjectAttendant() {
        return objectAttendant;
    }

    public void setObjectAttendant(MudObjectAttendant objectAttendant) {
        this.objectAttendant = objectAttendant;
    }

    public TelnetD getTelnetDaemon() {
        return telnetDaemon;
    }

    public void setTelnetDaemon(TelnetD telnetDeamon) {
        JMudEngine.telnetDaemon = telnetDeamon;
    }

    public CountingMap getNetDeadPlayers() {
        return netDeadPlayers;
    }

    public void setNetDeadPlayers(CountingMap netDeadPlayers) {
        this.netDeadPlayers = netDeadPlayers;
    }

    public void setShutdownRequested(boolean shutdownRequested) {
        this.shutdownRequested = shutdownRequested;
    }

    public boolean isShutdownRequested() {
        return shutdownRequested;
    }

    protected void shutdownNow() {
        // TODO Auto-generated method stub
        logger.info("shutdownNow called");
        destAllPlayers();
        getTelnetDaemon().stop();
        this.setRunning(false);
        logger.info("shudownNow complete");
        System.exit(0);
    }

    protected void destAllPlayers() {
        Iterator<LoginShell> x = new HashSet<LoginShell>(getObjectRegistry().getActivePlayerHandles()).iterator();
        while (x.hasNext()) {
            LoginShell shell = x.next();
            if (shell.getConnection().isActive()) {
                Player p = getObjectRegistry().getPlayerByHandle(shell);
                p.dest(false);
            }
        }
    }

    public abstract ShutdownBehaviour createShutdownBehaviour();

}
